/*
 * @Author: 李石
 * @Date: 2024-04-09 16:20:37
 * @LastEditors: lishi
 * @LastEditTime: 2024-07-16 18:38:48
 * @Description: 
 * Copyright (c) 2024 by ${lishi0105@163.com}, All Rights Reserved. 
 */
#include <string.h>
#include <iomanip>
#include <sstream>
#include "NaluParse.h"
#include "time_util.h"
#include "FrameData.hpp"

static std::string dump(const void* buf, size_t size)
{
    int len = 0;
    char* data = (char*)buf;
    std::string result = "nullptr";
    if (data == nullptr || size <= 0)return result;
    size_t total = size * 3 + 1;
    char* buff = new char[size * 3 + 1];
    memset(buff, 0, size * 3 + 1);
    for (size_t i = 0; i < size; i++) {
        len += snprintf(buff + len, total-len, "%.2x ", (data[i] & 0xff));
    }
    result = std::string(buff);
    delete[] buff;
    buff = nullptr;
    return result;
}

FrameData::FrameData(const mk_frame frame)
    : m_data(nullptr), m_size(0), m_dts(0), m_pts(0), m_prefixSize(0),
    keyFrame_(false), dropAble_(true), decodeAble_(false),
    configFrame_(false), m_codec_id(-1), codec_name_("")
{
    m_is_vaild = false;
    if (frame == nullptr) {
        std::cerr << "frame is nullptr" << std::endl;
        return;
    }
    auto data = mk_frame_get_data(frame);
    auto size = mk_frame_get_data_size(frame);
    if (data == nullptr || size == 0) {
		std::cerr << "data is nullptr or size is 0" << std::endl;
		return;
	}
    m_data = new uint8_t[size + 1];
    if (m_data == nullptr) {
        std::cout << "new failed" << std::endl;
        return;
    }
    m_size = size;
    memcpy(m_data, data, m_size);
    m_data[m_size] = 0;
    m_pts           = TimeUtil::GetMicroTimestamp();
    m_dts           = m_pts;
    m_prefixSize    = mk_frame_get_data_prefix_size(frame);
    auto flag       = mk_frame_get_flags(frame);
    m_codec_id      = mk_frame_codec_id(frame);
    codec_name_     = mk_frame_codec_name(frame);
    keyFrame_       = (flag & MK_FRAME_FLAG_IS_KEY);
    configFrame_    = (flag & MK_FRAME_FLAG_IS_CONFIG);
    dropAble_       = (flag & MK_FRAME_FLAG_DROP_ABLE);
    decodeAble_     = !(flag & MK_FRAME_FLAG_NOT_DECODE_ABLE);
    m_is_vaild = true;
}

FrameData::FrameData(const void *tmp, size_t size_tmp, const mk_frame frame)
    : m_data(nullptr), m_size(0), m_dts(0), m_pts(0), m_prefixSize(0),
    keyFrame_(false), dropAble_(true), decodeAble_(false),
    configFrame_(false), m_codec_id(-1), codec_name_("")
{
    m_is_vaild = false;
    if (frame == nullptr) {
        std::cerr << "frame is nullptr" << std::endl;
        return;
    }
    
    if (tmp == nullptr || size_tmp == 0) {
        std::cerr << "data is nullptr or size is 0" << std::endl;
        return;
    }
    uint8_t *data_tmp= (uint8_t *)tmp;
    int32_t start_len = 0;
    m_size = size_tmp;
    if(data_tmp[0] != 0x00 || data_tmp[1] != 0x00 || (data_tmp[2] != 0x00 && data_tmp[2] == 0x01)) {
        m_size = size_tmp + 4;
        start_len = 4;
    }
    m_data = new uint8_t[m_size + 1];
    if (this->m_data == nullptr) {
        std::cout << "new failed" << std::endl;
        return;
    }
    memcpy(m_data + start_len, data_tmp, size_tmp);
    m_data[m_size] = 0;
    m_pts           = TimeUtil::GetMicroTimestamp();
    m_dts           = m_pts;
    m_prefixSize    = mk_frame_get_data_prefix_size(frame);
    auto flag       = mk_frame_get_flags(frame);
    m_codec_id      = mk_frame_codec_id(frame);
    codec_name_     = mk_frame_codec_name(frame);
    configFrame_  = _configFrame(m_codec_id, this->m_data, m_size, &start_len);
    keyFrame_     = _keyframe(m_codec_id, this->m_data, m_size, &start_len);
    dropAble_     = _dropAble(m_codec_id, this->m_data, m_size, &start_len);
    decodeAble_   = _decodeAble(m_codec_id, this->m_data, m_size, &start_len);
    m_is_vaild = true;
}

FrameData::FrameData(const void *tmp, size_t size_tmp, int32_t codec_id_tmp, uint64_t pts_tmp)
    : m_data(nullptr), m_size(0), m_dts(0), m_pts(0), m_prefixSize(0),
    keyFrame_(false), dropAble_(true), decodeAble_(false),
    configFrame_(false), m_codec_id(-1), codec_name_("")
{
    m_is_vaild = false;
    if (tmp == nullptr || size_tmp == 0) {
        std::cerr << "data is nullptr or size is 0" << std::endl;
        return;
    }
    if(codec_id_tmp != 0 && codec_id_tmp != 1) {
        std::cerr << "codec_id_tmp is error" << std::endl;
        return;
    }
    uint8_t *data_tmp= (uint8_t *)tmp;
    int32_t start_len = 0;
    m_size = size_tmp;
    if(data_tmp[0] != 0x00 || data_tmp[1] != 0x00 || (data_tmp[2] != 0x00 && data_tmp[2] == 0x01)) {
        m_size = size_tmp + 4;
        start_len = 4;
    }
    m_data = new uint8_t[m_size + 1];
    if (this->m_data == nullptr) {
        std::cout << "new failed" << std::endl;
        return;
    }
    memcpy(m_data + start_len, data_tmp, size_tmp);
    m_data[m_size] = 0;
    this->m_dts         = pts_tmp;
    this->m_pts         = pts_tmp;
    this->m_codec_id    = codec_id_tmp;
    this->codec_name_   = (codec_id_tmp == 0? "H264" : "H265");
    this->configFrame_  = _configFrame(codec_id_tmp, this->m_data, m_size, &start_len);
    this->keyFrame_     = _keyframe(codec_id_tmp, this->m_data, m_size, &start_len);
    this->dropAble_     = _dropAble(codec_id_tmp, this->m_data, m_size, &start_len);
    this->decodeAble_   = _decodeAble(codec_id_tmp, this->m_data, m_size, &start_len);
    this->m_prefixSize  = start_len;
    m_is_vaild = true;
}

FrameData::FrameData(const void *tmp, size_t size_tmp, int32_t codec_id_tmp)
    : m_data(nullptr), m_size(0), m_dts(0), m_pts(0), m_prefixSize(0),
    keyFrame_(false), dropAble_(true), decodeAble_(false),
    configFrame_(false), m_codec_id(-1), codec_name_("")
{
    uint64_t pts = TimeUtil::GetMicroTimestamp();
    FrameData(tmp, size_tmp, codec_id_tmp, pts);
}

FrameData::~FrameData()
{
    if (m_data != nullptr) {
        delete[] m_data;
        m_data = nullptr;
    }
    m_size = 0;
    m_is_vaild = false;
}

std::ostream& operator<<(std::ostream& os, const FrameData::Ptr& Frame)
{
    size_t len = 10;
    if (Frame.get() == nullptr || Frame->data() == nullptr || Frame->size() == 0) {
        os << "nullptr";
        return os;
    }
    if (Frame->size() < 10) {
        len = Frame->size();
    }
    os << "[" << Frame->pts() 
        << ", " << Frame->dts()
        << ", codec:" << Frame->codec_name()
        << ", drop:" << (Frame->dropAble()?"true ":"false")  
        << ", key:" << (Frame->keyFrame()?"true ":"false") 
        << ", "<< std::setw(6) << std::right << Frame->size() 
        << "] : "<< dump(Frame->data(), len);

    return os;
}

std::string FrameData::toString() const
{
    std::ostringstream oss;
    size_t len = 10;
    if (this->data() == nullptr || this->size() == 0) {
        oss << "nullptr";
    } else {
        if (this->size() < 10) {
            len = this->size();
        }
        // 格式化字符串
        oss << "[" << this->pts() 
            << ", " << this->dts()
            << ", codec:" << this->codec_name()
            << ", drop:" << (this->dropAble()?"true ":"false")  
            << ", key:" << (this->keyFrame()?"true ":"false") 
            << ", config:" << (this->configFrame()?"true ":"false") 
            << ", "<< std::setw(6) << std::right << this->size() 
            << "] : "<< dump(this->data(), len);
    }
    return oss.str();
}